package indi.zjh.dp.orm;

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.dom4j.DocumentException;







import indi.zjh.dp.db.Configuration;
import indi.zjh.dp.db.Session;
import indi.zjh.dp.error.UnsuportedFieldException;
import indi.zjh.dp.util.StringUtil;

public class Query<T> {
	private static Logger logger = Logger.getLogger(Query.class);
	
	protected Class<?> self;
	protected StringBuffer buffer;
	//protected String tableName;
	
	protected List<String> conditions;
	protected List<String> orders;
	protected String limitCond;
	protected String lastQuerySql = null; // 保存着上一次执行用的sql语句, 用于调试
	
	protected Session session;
	
	class GetTask<T> implements Runnable
	{
		private Result<T> result;
		private Class<?> cls;
		private HashSet<Long> ids;
		private Session session;
		private CountDownLatch counter;
		public GetTask(CountDownLatch counter, Session sess, Result<T> result, Class<?> cls, HashSet<Long> ids)
		{
			this.counter = counter;
			this.session = sess;
			this.result = result;
			this.cls = cls;
			this.ids = ids;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub

//			Iterator<T> iter = result.iterator();
//			while(iter.hasNext())
//			{
//				
//				Business obj = (Business)iter.next();
//				if(!this.ids.contains(obj.getId()))
//					continue;
//				try {
//					MethodFactory.SessionGetMethod.invoke(session, obj);
//				} catch (IllegalAccessException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} catch (IllegalArgumentException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} catch (InvocationTargetException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			}
//			
//			this.counter.countDown();
		}
	}

	public Query(Session sess)
	{
		this.session = sess;
	}
	
	public Query<T> find(Class<?> cls) throws UnsuportedFieldException, DocumentException, Exception
	{
		this.conditions = new ArrayList<String>();
		this.orders = new ArrayList<String>();
		this.limitCond = "";
		this.self = cls;
		this.buffer = new StringBuffer();
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(cls.getName());
		String tableName = BusinessMapDataStrategy.getTableName(rc);
		this.buffer.append("SELECT DISTINCT `"+tableName+"`.`_id` FROM `"+tableName+"`");
		this.conditions.add("`"+tableName+"`.`_deleted` = 0");
		return this;
	}
	
	public Query<T> find(Class<?> cls, String cond) throws UnsuportedFieldException, DocumentException, Exception
	{
		this.conditions = new ArrayList<String>();
		this.orders = new ArrayList<String>();
		this.limitCond = "";
		this.self = cls;
		this.buffer = new StringBuffer();
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(cls.getName());
		String tableName = BusinessMapDataStrategy.getTableName(rc);
		this.buffer.append("SELECT DISTINCT `"+tableName+"`.`_id` FROM `"+tableName+"`");
		String newCond = cond.replaceAll("(`[_a-zA-Z]+`)", "`"+tableName+"`.$1");
		this.conditions.add(newCond);
		this.conditions.add("`"+tableName+"`.`_deleted` = 0");
		//System.out.println("匹配条件:"+newCond);
		return this;
	}
	
	public Query<T> order(Class<?> cls, String orderstr) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(cls.getName());
		String tableName = BusinessMapDataStrategy.getTableName(rc);
		String newOrderstr = orderstr.replaceAll("(`[_a-zA-Z]+`)", "`"+tableName+"`.$1");
		this.orders.add(newOrderstr);
		return this;
	}
	
	public Query<T> limit(int start, int count)
	{
		this.limitCond = "LIMIT "+start+", "+count;
		return this;
	}
	
	public Query<T> extend(Class<?> son, Class<?> father) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(son.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(father.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.EXTEND);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	public Query<T> extend(Class<?> son, Class<?> father, String cond) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(son.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(father.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		
		String newCond = cond.replaceAll("(`[_a-zA-Z]+`)", "`"+tbname+"`.$1");
		this.conditions.add(newCond);
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.EXTEND);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	
	public Query<T> composition(Class<?> container, Class<?> contained) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(container.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(contained.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.COMPOSITION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}

	
	public Query<T> composition(Class<?> container, Class<?> contained, String cond) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(container.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(contained.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		
		String newCond = cond.replaceAll("(`[_a-zA-Z]+`)", "`"+tbname+"`.$1");
		this.conditions.add(newCond);
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.COMPOSITION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	
	public Query<T> aggregation(Class<?> group, Class<?> single) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(group.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(single.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.AGGREGATION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	public Query<T> aggregation(Class<?> group, Class<?> single, String cond) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(group.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(single.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		
		String newCond = cond.replaceAll("(`[_a-zA-Z]+`)", "`"+tbname+"`.$1");
		this.conditions.add(newCond);
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.AGGREGATION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	
	public Query<T> association(Class<?> assc, Class<?> assced) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(assc.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(assced.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.ASSOCIATION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	public Query<T> association(Class<?> assc, Class<?> assced, String cond) throws UnsuportedFieldException, DocumentException, Exception
	{
		RelationClass mainrc = Configuration.getInstance().getVersion().getRelationClass(assc.getName());
		String mainTablenName = BusinessMapDataStrategy.getTableName(mainrc);
		
		RelationClass rc = Configuration.getInstance().getVersion().getRelationClass(assced.getName());
		String tbname = BusinessMapDataStrategy.getTableName(rc);
		String reltbname = mainTablenName+"_"+tbname;
		this.buffer.append(" LEFT JOIN `"+reltbname+"` ON `"+mainTablenName+"`.`_id` = `"+reltbname+"`.`aid`");
		this.buffer.append(" LEFT JOIN `"+tbname+"` ON `"+reltbname+"`.`bid` = `"+tbname+"`.`_id`");
		
		String newCond = cond.replaceAll("(`[_a-zA-Z]+`)", "`"+tbname+"`.$1");
		this.conditions.add(newCond);
		this.conditions.add("`"+reltbname+"`.`_t` = "+RelationStrategy.ASSOCIATION);
		this.conditions.add("`"+tbname+"`.`_deleted` = 0");
		return this;
	}
	
	
	public Result<T> result() throws SQLException, DocumentException, Exception
	{
		long stime = System.currentTimeMillis();
		Result<T> result = new Result<T>();
		//Session session = Configuration.getSessionFactory().createSession();

		if(this.conditions.size() > 0)
		{
			this.buffer.append(" WHERE ");
			this.buffer.append(String.join(" AND ", this.conditions));
		}
		
		if(this.orders.size() > 0)
		{
			this.buffer.append(" ORDER BY ");
			this.buffer.append(String.join(", ", this.orders));
		}
		
		if(this.limitCond.length() > 0)
		{
			this.buffer.append(" "+this.limitCond);
		}
		
		this.lastQuerySql = this.buffer.toString();
		List<Map<String, Object>> ids = session.query(this.buffer.toString());
		logger.info("找到记录数:"+ids.size());
		int len = ids.size();
		
		if(Configuration.isMysql())
		{
			for(int i = 0; i < len; ++i)
			{
				long id = (long)ids.get(i).get("_id");
				result.addObject((T)session.getById(this.self, id));
			}
		}
		else
		{
			for(int i = 0; i < len; ++i)
			{
				int id = (int)ids.get(i).get("_id");
				result.addObject((T)session.getById(this.self, id));
			}
		}

/*
		int len = ids.size();
		HashSet<Long> ids1 = new HashSet<Long>();
		for(int i = 0; i < len; ++i)
		{
			long id = (long)ids.get(i).get("_id");
			Business newobj = (Business)this.self.newInstance();
			newobj.setId(id);
			result.addObject((T)newobj);
			ids1.add(id);
		}
		
		
		CountDownLatch counter = new CountDownLatch(1);
		
		
		GetTask<T> task1 = new GetTask<T>(counter, session, result, this.self, ids1);

		(new Thread(task1)).start();
		
		counter.await();
*/		
		//session.close();
		long consume = System.currentTimeMillis() - stime;
		logger.info("Search consume "+consume+" 毫秒");
		return result;
	}
	
	public Result<Long> idResult() throws SQLException, DocumentException, Exception
	{
		long stime = System.currentTimeMillis();
		Result<Long> result = new Result<Long>();
		//Session session = Configuration.getSessionFactory().createSession();

		if(this.conditions.size() > 0)
		{
			this.buffer.append(" WHERE ");
			this.buffer.append(String.join(" AND ", this.conditions));
		}
		
		if(this.orders.size() > 0)
		{
			this.buffer.append(" ORDER BY ");
			this.buffer.append(String.join(", ", this.orders));
		}
		
		if(this.limitCond.length() > 0)
		{
			this.buffer.append(" "+this.limitCond);
		}
		
		this.lastQuerySql = this.buffer.toString();
		List<Map<String, Object>> ids = session.query(this.buffer.toString());
		int len = ids.size();
		for(int i = 0; i < len; ++i)
		{
			long id = (long)ids.get(i).get("_id");
			result.addObject(id);
			//result.addObject((T)session.getById(this.self, id));
		}
		//session.close();
		long consume = System.currentTimeMillis() - stime;
		logger.info("Search consume "+consume+" 毫秒");
		return result;
	}
	
	public Result<T> result(Condition cond) throws SQLException, DocumentException, Exception
	{
		long stime = System.currentTimeMillis();
		Result<T> result = new Result<T>();
		//Session session = Configuration.getSessionFactory().createSession();

		if(this.conditions.size() > 0)
		{
			this.buffer.append(" WHERE ");
			this.buffer.append(String.join(" AND ", this.conditions));
		}
		
	
		List<Map<String, Object>> ids = session.query(this.buffer.toString());
		//System.out.println(StringUtil.getString(ids));
		int len = ids.size();
		for(int i = 0; i < len; ++i)
		{
			long id = (long)ids.get(i).get("_id");
			Business obj = (Business)session.getById(this.self, id);
			if(cond.test(obj))
			{
				result.addObject((T)obj);
			}
		}
		//session.close();
		long consume = System.currentTimeMillis() - stime;
		logger.info("Search consume "+consume+" 毫秒");
		return result;
	}
	
	public String getLastQuerySql()
	{
		if(this.lastQuerySql == null)
		{
			return "";
		}
		
		return this.lastQuerySql;
	}
}
